//

#ifndef Polygon2D_h__
#define Polygon2D_h__

#include "Point2D.h"
#include "Vector2D.h"


namespace Geometric
{
	class _GEOMETRIC Polygon2D
	{
	public:
		typedef std::vector<Point2D> PointVec;

	public:
		Polygon2D() :m_point_vec()
		{
		}

		Polygon2D(const Polygon2D& _polygon)
			:m_point_vec(_polygon.m_point_vec)
		{
		}

		~Polygon2D()
		{
		}

	public:
		inline static bool isInTriangle(const Point2D& _a, const Point2D& _b,
			const Point2D& _c, const Point2D& _point)
		{
			return false;
		}

		inline static bool isInQuadril(const Point2D& _a, const Point2D& _b,
			const Point2D& _c, const Point2D& _d, const Point2D& _point)
		{
			return false;
		}

	public:
		inline bool isValid()
		{
			return true;
		}

	public:
		inline void insertPoint(const Point2D& _point)
		{
			m_point_vec.push_back(_point);
		}

		inline unsigned int countPoints()
		{
			return m_point_vec.size();
		}

		inline void setPoint(unsigned int _index, double _x, double _y)
		{
			m_point_vec[_index].m_x = _x;
			m_point_vec[_index].m_y = _y;
		}

		//???
		inline bool isClockwise()
		{
			// if points list is clockwise
			return true;
		}

		// note: input polygon points is counterclockwise
		inline int getPolygonType()
		{
			if(isValid() == true)
			{
				unsigned int pt_count = countPoints();
				for(unsigned int i = 0; i < pt_count; i++)
				{
					const Point2D& start = m_point_vec[(i+0)%pt_count];
					const Point2D& mid = m_point_vec[(i+1)%pt_count];
					const Point2D& end = m_point_vec[(i+2)%pt_count];
					Vector2D vec1(mid, start), vec2(mid, end);

					if(Gmath::isLess(vec1.getRotateAngle(vec2), MATH_CONST_PI) == false)
					{
						// is concave polygon
						return POLYGON_TYPE_CONCAVE;
					}
				}
				// is convex polygon
				return POLYGON_TYPE_CONVEX;
			}
			// invalid
			return POLYGON_TYPE_INVALID;
		}

		// note: input polygon points is counterclockwise
		inline bool isInside(const Point2D& _point)
		{
			int type = getPolygonType();
			double angle = 0;

			if(type == POLYGON_TYPE_CONVEX)
			{
				// case 1: convex polygon
				unsigned int pt_count = countPoints();
				for(unsigned int i = 0; i < pt_count; i++)
				{
					const Point2D& start = m_point_vec[(i+0)%pt_count];
					const Point2D& mid = _point;
					const Point2D& end = m_point_vec[(i+1)%pt_count];
					Vector2D vec1(mid, start), vec2(mid, end);

					// sum all the angles
					double tmp = vec1.getRotateAngle(vec2, VECTOR_CLW_REVERSE);
					angle += (Gmath::isLess(tmp, MATH_CONST_PI) == true) ? tmp : (MATH_CONST_2PI - tmp);
				}
			}
			else if(type == POLYGON_TYPE_CONCAVE)
			{
				// case 2: concave polygon
				PointVec pt_vec;
				unsigned int pt_count = countPoints();
				for(unsigned int i = 0; i < pt_count; i++)
				{
					const Point2D& start = m_point_vec[(i+0)%pt_count];
					const Point2D& mid = m_point_vec[(i+1)%pt_count];
					const Point2D& end = m_point_vec[(i+2)%pt_count];
					Vector2D vec1(mid, start), vec2(mid, end);

					if(Gmath::isLess(vec1.getRotateAngle(vec2, VECTOR_CLW_REVERSE), MATH_CONST_PI) == false)
					{
						// collect points: angle<PI, angle>PI: do nothing
						pt_vec.push_back(mid);
					}
					else
					{
						// if the point is locate on discard point, return false
						if(mid == _point)
						{
							return false;
						}
					}
				}

				// get sum of angles
				pt_count = pt_vec.size();
				for(unsigned int i = 0; i < pt_count; i++)
				{
					const Point2D& start = pt_vec[(i+0)%pt_count];
					const Point2D& mid = _point;
					const Point2D& end = pt_vec[(i+1)%pt_count];
					Vector2D vec1(mid, start), vec2(mid, end);

					// sum all the angles
					double tmp = vec1.getRotateAngle(vec2, VECTOR_CLW_REVERSE);
					angle += (Gmath::isLess(tmp, MATH_CONST_PI) == true) ? tmp : (MATH_CONST_2PI-tmp);
				}
			}
			else
			{
				// invalid polygon, return false
				return false;
			}

			// judge: angle=PI ?
			const double accuracy = 1.0e-8;
			if(Gmath::isEqual(angle, MATH_CONST_2PI, accuracy) == true)
			{
				// angle=PI
				return true;
			}
			else
			{
				// anlge!=PI
				return false;
			}
		}

	public:
		// store points of polygon
		PointVec m_point_vec;
	};

}

#endif
